<?php
/*
*
* dtapi 类容器
 * 实现依赖注入和反射，功能实现的比较简单，因为DTapi不是一个完整的开发框架,只是destoon的一个插件开发脚手架，所以尽量做到代码精简
* 项目官网： www.dtapi.cn
* 这是一个完全开源免费的项目，您可以任意使用；我们唯一的要求就是保留本信息，当然您也可以在后面添加自己的版权信息，我们支持任何人使用本软件进行任何商业的非商业用途。
* 如果您有问题或者有更好的想法可以加群 61291160 一起讨论。
* -- 高飞
*
*/
namespace dtapi;
use Exception;
use ReflectionClass;
use ReflectionMethod;
class window
{
    /**
     * @param       $class
     * @param array $parameters
     * @return object
     * @throws \ReflectionException
     * @throws Exception
     */
    public static function make($class, $parameters = [])
    {
        // 通过反射获取反射类
        $rel_class = new ReflectionClass($class);
        // 查看是否可以实例化
        if (! $rel_class->isInstantiable())
        {
            throw new Exception($class . ' 类不可实例化');
        }
        // 查看是否用构造函数
        $rel_method = $rel_class->getConstructor();
        // 没有构造函数的话，就可以直接 new 本类型了
        if (is_null($rel_method))
        {
            return new $class();
        }

        // 有构造函数的话就获取构造函数的参数
        $dependencies = $rel_method->getParameters();
        // 处理，把传入的索引数组变成关联数组， 键为函数参数的名字
        foreach ($parameters as $key => $value)
        {

            if (is_numeric($key))
            {
                // 删除索引数组， 只留下关联数组
                unset($parameters[$key]);
                // 用参数的名字做为键
                $parameters[$dependencies[$key]->name] = $value;
            }
        }
        // 处理依赖关系
        $actual_parameters = [];
        foreach ($dependencies as $dependenci)
        {
            // 获取对象名字，如果不是对象返回 null
            $class_name = $dependenci->getClass();
            // 获取变量的名字
            $var_name = $dependenci->getName();
            // 如果是对象， 则递归new
            if (array_key_exists($var_name, $parameters))
            {
                $actual_parameters[] = $parameters[$var_name];
            }
            elseif (is_null($class_name))
            {
                // null 则不是对象，看有没有默认值， 如果没有就要抛出异常
                if (! $dependenci->isDefaultValueAvailable())
                {
                    throw new Exception($var_name . ' 参数没有默认值');
                }
                $actual_parameters[] = $dependenci->getDefaultValue();
            }
            else
            {
                $actual_parameters[] = self::make($class_name->getName());
            }
        }

        // 获得构造函数的数组之后就可以实例化了
        return $rel_class->newInstanceArgs($actual_parameters);
    }
    /**
     * 动态运行对象的函数
     *
     * @param       $class
     * @param       $method
     * @param array $class_parameters
     * @param array $method_parameters
     * @return mixed
     * @throws \ReflectionException
     * @throws Exception
     */
    public static function run($class, $method, $class_parameters = [], $method_parameters = [])
    {
        $object = self::make($class, $class_parameters);

        // 动态获取一个方法的参数
        $rel_method = new ReflectionMethod($object, $method);
        // 获取方法所需的参数
        $dependencies = $rel_method->getParameters();
        // 处理，把传入的索引数组变成关联数组， 键为函数参数的名字
        foreach ($method_parameters as $key => $value)
        {
            if (is_numeric($key))
            {
                // 删除索引数组， 只留下关联数组
                unset($method_parameters[$key]);
                // 用参数的名字做为键
                $method_parameters[$dependencies[$key]->name] = $value;
            }
        }
        // 处理依赖关系
        $actual_parameters = [];
        foreach ($dependencies as $dependenci)
        {
            // 获取对象名字，如果不是对象返回 null
            $class_name = $dependenci->getClass();
            // 获取变量的名字
            $var_name = $dependenci->getName();
            // 如果是对象， 则递归new
            if (array_key_exists($var_name, $method_parameters))
            {
                $actual_parameters[] = $method_parameters[$var_name];
            }
            elseif (is_null($class_name))
            {
                // null 则不是对象，看有没有默认值， 如果没有就要抛出异常
                if (! $dependenci->isDefaultValueAvailable())
                {
                    throw new Exception($var_name . ' 参数没有默认值');
                }
                $actual_parameters[] = $dependenci->getDefaultValue();
            }
            else
            {
                $actual_parameters[] = self::make($class_name->getName());
            }
        }
        // 运行方法
        return $rel_method->invokeArgs($object, $actual_parameters);

    }
}
?>